/* Includes ----------------------------------------------------------*/
#include "w25q.h"
/* define ------------------------------------------------------------*/
/* typedef -----------------------------------------------------------*/
/* macro -------------------------------------------------------------*/
/* variables ---------------------------------------------------------*/
/* function prototypes -----------------------------------------------*/
int W25Q_SendCmd(uint8_t cmd);
int W25Q_SendCmdWithContent(uint8_t cmd, uint8_t *content, size_t length);
int W25Q_ReceiveCmd(uint8_t cmd, uint8_t *rx, size_t length);
int W25Q_ReceiveCmdArray(uint8_t *cmd, size_t cmdLength, uint8_t *rx, size_t length);

extern int W25Q_SpiWrite(void *data, size_t length);
extern int W25Q_SpiRead(void *data, size_t length);
extern int W25Q_Control(bool isEnable);

extern uint32_t NETimer_Get(void);


/*********************************************************************************************
 
    写flash, 按照每页写(256字节)
 
  ********************************************************************************************/
int W25Q_WritePage(uint32_t address, uint8_t *data, size_t length)
{
    uint8_t cmd[4], *p = (uint8_t*)&address, status;
    int result = -1;
    uint32_t _time = 0;
    
    W25Q_SendCmd(0x06);               // Write Enable
    
    cmd[0] = 0x02;
    cmd[1] = p[2];
    cmd[2] = p[1];
    cmd[3] = p[0];
  
    W25Q_Control(true);
    if((result = W25Q_SpiWrite(cmd, 4)) != 0)
    {   goto end;   }
    if((result = W25Q_SpiWrite(data, length)) != 0)
    {   goto end;   }
    W25Q_Control(false);
    
    _time = NETimer_Get();
    while(true)
    {
        result = W25Q_ReceiveCmd(0x05, &status, 1);
        if((status & W25Q_STATUS_BUSY) == 0)
        {   break;  }
        if((_time + 5) < NETimer_Get())
        {   result = -2;   }
    }
    
    result = 0;
end:
    W25Q_SendCmd(0x04);                // Write Disable
    
    return result;
}
/*********************************************************************************************
 
    w25q write, unit is byte
 
  ********************************************************************************************/
int W25Q_Write(uint32_t address, uint8_t *data, size_t length)
{
    int32_t lengthSingle = 0, lengthSingleMax = 0;

    while(length > 0)
    {
        lengthSingleMax = 256 - (address & 0x000000FF);
      
        lengthSingle = (lengthSingleMax > length)? length: lengthSingleMax;
        
        W25Q_WritePage(address, data, lengthSingle);
              
        /* sync other coef */
        data += lengthSingle;
        length -= lengthSingle;
        address += lengthSingle;
    }
    
    return 0;
}

/*********************************************************************************************
 
    擦写flash, 4种模式, 4k 32k 64k chip
 
  ********************************************************************************************/
int W25Q_Erase(enum W25QEarseType earseType, uint32_t address)
{
    uint8_t cmd[4], *p = (uint8_t*)&address, status;
    int result = 0;
    
    W25Q_SendCmd(0x06);               // Write Enable
    
    switch(earseType)
    {
    case W25QEarse_Sector4K:
        cmd[0] = 0x20;
        break;
    case W25QEarse_Block32k:
        cmd[0] = 0x52;
        break;
    case W25QEarse_Block64k:
        cmd[0] = 0xD8;
        break;
    case W25QEarse_Chip: 
        cmd[0] = 0xC7;
        break;
    }
    cmd[1] = p[2];
    cmd[2] = p[1];
    cmd[3] = p[0];
    
    W25Q_Control(true);
    result = W25Q_SpiWrite(cmd, 1);
    if(earseType != W25QEarse_Chip)
    {   result = W25Q_SpiWrite(cmd + 1, 3);  }
    W25Q_Control(false);
    
    while(true)
    {
        result = W25Q_ReceiveCmd(0x05, &status, 1);
        if((status & W25Q_STATUS_BUSY) == 0)
        {   break;  }
    }
    
    W25Q_SendCmd(0x04);                // Write Disable
    
    return result;
}

/*********************************************************************************************
 
    flash 读
 
  ********************************************************************************************/
int W25Q_Read(uint32_t address, uint8_t *data, size_t length)
{
    uint8_t cmd[4], *p = (uint8_t*)&address;
    int result = 0;
    
    cmd[0] = 0x03;
    cmd[1] = p[2];
    cmd[2] = p[1];
    cmd[3] = p[0];

    W25Q_Control(true);
    result = W25Q_SpiWrite(cmd, 4);
    result = W25Q_SpiRead(data, length);
    W25Q_Control(false);
    
    return result;
}

/*********************************************************************************************
 
    发送命令处理
 
  ********************************************************************************************/
int W25Q_SendCmd(uint8_t cmd)
{
    int result = 0;
    W25Q_Control(true);
    result = W25Q_SpiWrite(&cmd, 1);
    W25Q_Control(false);
    
    return result;
}
int W25Q_SendCmdWithContent(uint8_t cmd, uint8_t *content, size_t length)
{
    int result = 0;
    W25Q_Control(true);
    result = W25Q_SpiWrite(&cmd, 1);
    result = W25Q_SpiWrite(content, length);
    W25Q_Control(false);
    
    return result;
}
/*********************************************************************************************
 
    读取命令
 
  ********************************************************************************************/
int W25Q_ReceiveCmd(uint8_t cmd, uint8_t *rx, size_t length)
{
    int result = 0;
    W25Q_Control(true);
    result = W25Q_SpiWrite(&cmd, 1);
    result = W25Q_SpiRead(rx, length);
    W25Q_Control(false);
    return result;
}
int W25Q_ReceiveCmdArray(uint8_t *cmd, size_t cmdLength, uint8_t *rx, size_t length)
{
    int result = 0;
    W25Q_Control(true);
    result = W25Q_SpiWrite(cmd, cmdLength);
    result = W25Q_SpiRead(rx, length);
    W25Q_Control(false);
    return result;
}
